
#include "BodyManager.h"

//-------------------
//--- BodyManager ---
//-------------------

void BodyManager::synchronizeSBodies(size_t lo, size_t hi)
{
	for (size_t i = lo; i < hi; i++)
	{
		sbodies.bodies[i].position = vec3f(ibodies.positions.getX(i),
										   ibodies.positions.getY(i),
										   ibodies.positions.getZ(i));
		sbodies.bodies[i].velocity = vec3f(ibodies.velocities.getX(i),
										   ibodies.velocities.getY(i),
										   ibodies.velocities.getZ(i));
		sbodies.bodies[i].acceleration = vec3f(ibodies.accelerations.getX(i),
											   ibodies.accelerations.getY(i),
											   ibodies.accelerations.getZ(i));
		sbodies.bodies[i].mass = ibodies.masses[i];
	}
}

void BodyManager::synchronizeIBodies(size_t lo, size_t hi)
{
	for (size_t i = lo; i < hi; i++)
	{
		ibodies.positions.getX(i) = sbodies.bodies[i].position.x;
		ibodies.positions.getY(i) = sbodies.bodies[i].position.y;
		ibodies.positions.getZ(i) = sbodies.bodies[i].position.z;
		ibodies.velocities.getX(i) = sbodies.bodies[i].velocity.x;
		ibodies.velocities.getY(i) = sbodies.bodies[i].velocity.y;
		ibodies.velocities.getZ(i) = sbodies.bodies[i].velocity.z;
		ibodies.accelerations.getX(i) = sbodies.bodies[i].acceleration.x;
		ibodies.accelerations.getY(i) = sbodies.bodies[i].acceleration.y;
		ibodies.accelerations.getZ(i) = sbodies.bodies[i].acceleration.z;
		ibodies.masses[i] = sbodies.bodies[i].mass;
	}
}

void BodyManager::checkSize()
{
	if (size != sbodies.getSize())
	{
		size.lock();
		size_t old_size = sbodies.getSize();
		sbodies.resize(size);
		ibodies.resize(size);
		if (old_size < (size_t)size)
			distributeBodies(old_size, size);
		size.unLock();
	}
}

void BodyManager::synchronize(bool separated)
{
	if (is_sbodies_actual ^ separated)
	{
		if (separated)
		{
			synchronizeSBodies(0, sbodies.getSize());
			is_sbodies_actual = true;
		}
		else
		{
			synchronizeIBodies(0, sbodies.getSize());
			is_sbodies_actual = false;
		}
	}
	checkSize();
	if (should_reset)
	{
		distributeBodies(0, sbodies.getSize());
		should_reset = false;
	}
}

void BodyManager::distributeBodies_Random(size_t lo, size_t hi)
{
	for (size_t i = lo; i < hi; i++)
	{
		float phi = getFloatRandom(0.0f, 2 * TTG_PI);
		float theta = getFloatRandom(-TTG_PI, TTG_PI);
		float rho = getFloatRandom(0.0f, 1.0f);
		sbodies.bodies[i].mass = getFloatRandom(MIN_MASS, MAX_MASS);
		sbodies.bodies[i].position = vec3f(cosf(theta) * cosf(phi),
										   sinf(theta),
										   cosf(theta) * sinf(phi)) * rho;
		sbodies.bodies[i].velocity = vec3f::ZERO;
		sbodies.bodies[i].acceleration = vec3f::ZERO;
	}
	synchronizeIBodies(lo, hi);
}

void BodyManager::distributeBodies_Circle(size_t lo, size_t hi)
{
	for (size_t i = lo; i < hi; i++)
	{
		float phi = 2 * TTG_PI * (i - lo) / (hi - lo);
		sbodies.bodies[i].mass = getFloatRandom(MIN_MASS, MAX_MASS);
		sbodies.bodies[i].position = vec3f(cosf(phi), sinf(phi), 0.0f);
		sbodies.bodies[i].velocity = vec3f::ZERO;
		sbodies.bodies[i].acceleration = vec3f::ZERO;
	}
	synchronizeIBodies(lo, hi);
}

void BodyManager::distributeBodies(size_t lo, size_t hi)
{
	if (distribution_type == distribution_type["Random"])
		distributeBodies_Random(lo, hi); else
	if (distribution_type == distribution_type["Circle"])
		distributeBodies_Circle(lo, hi);
}

BodyManager::BodyManager(size_t count)
	:size("Count of bodies", 1, 32 * 1024),
	 distribution_type("Distribution type", "Random|Circle"),
	 should_reset("reset?")
{
	size = (int)count;
	should_reset = false;
	checkSize();
	is_sbodies_actual = true;
}
